package wci.frontend.pascal.parsers;

import static wci.frontend.pascal.PascalErrorCode.IDENTIFIER_REDEFINED;
import static wci.frontend.pascal.PascalErrorCode.MISSING_COMMA;
import static wci.frontend.pascal.PascalErrorCode.MISSING_IDENTIFIER;
import static wci.frontend.pascal.PascalErrorCode.MISSING_RIGHT_PAREN;
import static wci.frontend.pascal.PascalTokenType.COMMA;
import static wci.frontend.pascal.PascalTokenType.IDENTIFIER;
import static wci.frontend.pascal.PascalTokenType.RIGHT_PAREN;
import static wci.frontend.pascal.PascalTokenType.SEMICOLON;
import static wci.intermediate.symtabimpl.DefinitionImpl.ENUMERATION_CONSTANT;
import static wci.intermediate.symtabimpl.SymTabKeyImpl.CONSTANT_VALUE;
import static wci.intermediate.typeimpl.TypeFormImpl.ENUMERATION;
import static wci.intermediate.typeimpl.TypeKeyImpl.ENUMERATION_CONSTANTS;

import java.util.ArrayList;
import java.util.EnumSet;

import wci.frontend.Token;
import wci.frontend.TokenType;
import wci.frontend.pascal.PascalParserTD;
import wci.frontend.pascal.PascalTokenType;
import wci.intermediate.SymTabEntry;
import wci.intermediate.TypeFactory;
import wci.intermediate.TypeSpec;

/**
 * <h1>EnumerationTypeParser</h1>
 * <p>解析简单类型里面的枚举类型</p>
 * <p>Copyright (c) 2009 by Ronald Mak</p>
 * <p>For instructional purposes only.  No warranties.</p>
 */
public class EnumerationTypeParser extends TypeSpecificationParser
{
    protected EnumerationTypeParser(PascalParserTD parent)
    {
        super(parent);
    }
    //左括号之后，开始一个枚举的同步集
    private static final EnumSet<PascalTokenType> ENUM_CONSTANT_START_SET =
        EnumSet.of(IDENTIFIER, COMMA);

    //一个枚举定义后的同步集
    private static final EnumSet<PascalTokenType> ENUM_DEFINITION_FOLLOW_SET =
        EnumSet.of(RIGHT_PAREN, SEMICOLON);
    static {
        ENUM_DEFINITION_FOLLOW_SET.addAll(DeclarationsParser.VAR_START_SET);
    }

    public TypeSpec parse(Token token)
        throws Exception
    {
        TypeSpec enumerationType = TypeFactory.createType(ENUMERATION);
        int value = -1;
        ArrayList<SymTabEntry> constants = new ArrayList<SymTabEntry>();

        token = nextToken();  // 吞噬掉开头的左括号。

        do {
            token = synchronize(ENUM_CONSTANT_START_SET);
            parseEnumerationIdentifier(token, ++value, enumerationType,
                                       constants);

            token = currentToken();
            TokenType tokenType = token.getType();

            // 枚举常量之间的逗号
            if (tokenType == COMMA) {
                token = nextToken(); 

                if (ENUM_DEFINITION_FOLLOW_SET.contains(token.getType())) {
                    errorHandler.flag(token, MISSING_IDENTIFIER, this);
                }
            }
            else if (ENUM_CONSTANT_START_SET.contains(tokenType)) {
                errorHandler.flag(token, MISSING_COMMA, this);
            }
        } while (!ENUM_DEFINITION_FOLLOW_SET.contains(token.getType()));

        // 结束的右括号
        if (token.getType() == RIGHT_PAREN) {
            token = nextToken();  
        }
        else {
            errorHandler.flag(token, MISSING_RIGHT_PAREN, this);
        }

        enumerationType.setAttribute(ENUMERATION_CONSTANTS, constants);
        return enumerationType;
    }

    /**
     * 解析一个枚举标识符即 enumxx(a,b,c)中的a,b或者c
     * @param token 当前token
     * @param value 标识符的对应的整数索引值，以0起步
     * @param enumerationType 对应的枚举类型即enumxx
     * @param constants 枚举常量标识符的容器
     * @throws Exception
     */
    private void parseEnumerationIdentifier(Token token, int value,
                                            TypeSpec enumerationType,
                                            ArrayList<SymTabEntry> constants)
        throws Exception
    {
        TokenType tokenType = token.getType();

        if (tokenType == IDENTIFIER) {
            String name = token.getText().toLowerCase();
            SymTabEntry constantId = symTabStack.lookupLocal(name);
            //这个常量一定是没有定义过的
            if (constantId != null) {
                errorHandler.flag(token, IDENTIFIER_REDEFINED, this);
            }
            else {
                constantId = symTabStack.enterLocal(name);
                constantId.setDefinition(ENUMERATION_CONSTANT);
                constantId.setTypeSpec(enumerationType);
                constantId.setAttribute(CONSTANT_VALUE, value);
                constantId.appendLineNumber(token.getLineNumber());
                constants.add(constantId);
            }

            token = nextToken(); //继续
        }
        else {
            errorHandler.flag(token, MISSING_IDENTIFIER, this);
        }
    }
}
